/**
 * @author: Gene
 * @age: 永远18岁的美少年
 * @Email： yangjianyun@58.com
 * @date: 2021-12-01 22:03:33
 * @description:  ts 中的装饰器
 */
/**
 * @param constructor: 装饰器中,有一个参数constructor, 类型是 Function,即,构造函数
 */
namespace A {
    /** ============ 0类装饰器 ========*/
    function addNameEat(constructor: Function) {
        constructor.prototype.name = "Gene"
        constructor.prototype.eat = function () {
        }
    }

    @addNameEat
    class Person {
        name: string
        eat: Function

        constructor() {
        }
    }

    let p: Person = new Person()
    console.log(p.name);
    p.eat()


}

// 添加命名空间, 以免命名冲突
// 类型安全
namespace B {
    /** =========== 类装饰器工厂  =============== */
    function addNameEatFactory(name: string) {
        return function addNameEat(constructor: Function) {
            constructor.prototype.name = name
            constructor.prototype.eat = function () {
            }
        }
    }


    @addNameEatFactory("yang")
    class Person {
        name: string
        eat: Function

        constructor() {
        }
    }

    let p1: Person = new Person()
    // p1.eat()

}

// ts中的类的装饰器
namespace C {
    // 替换类 返回一个新的类, 替换旧的类, 但是参数可以多添加, 但是不能少于老的类里面的参数
    function replaceClass(constructor: Function) {
        return class {
            name: string
            eat: Function

            constructor() {
            }
        }
    }

    @replaceClass
    class Person {
        name: string
        eat: Function

        constructor() {
        }
    }

    //
    // let p1: Person = new Person()
    // p1.eat()

}

/** ================ ts 中的属性装饰器  =======================*/
// 属性装饰器 =>
// 1. 可以装饰属性
// 2. 可以装饰方法
namespace D {
    /**
     *  装饰器装饰属性, 接收三个参数.
     * @param target 如果装饰的是 实例属性的话, 那么target是我们的钩子函数的原型
     * @param propertyKey
     */
    function upperCase(target: any, propertyKey: string) {
        let value = target[propertyKey]
        const getter = () => value
        const setter = (newVal: string) => {
            value = newVal.toUpperCase()
        }
        if (delete target[propertyKey]) {
            Object.defineProperty(target, propertyKey, {
                get: getter,
                set: setter,
                enumerable: true,
                configurable: true
            })
        }
    }

    // 如果装饰的是静态属性的话, 那么 target 是 构造函数本身
    function staticPropertyDecorator(target: any, propertyKey: string) {
        console.log(target, propertyKey);
    }

    /**
     * @param target 实例方法的装饰器
     * @param propertyKey
     * @param descriptor 属性描述器
     */
    function noEnumerable(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log(target, propertyKey, descriptor);
        descriptor.enumerable = false
    }

    function toNumber(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        let oldMethod = descriptor.value
        descriptor.value = function (...args: any[]) {
            args = args.map((item) => parseFloat(item))
            return oldMethod.apply(this, args)
        }
    }

    class Person {
        @upperCase
        name: string = "Gene123" // 实例属性
        @staticPropertyDecorator
        public static age: number = 10 // 静态属性

        @noEnumerable
        getName() { // 实例方法
            console.log(this.name);
        }

        @toNumber
        sum(...args: any[]) { // 实例方法
            return args.reduce((accu: number, item: number) => {
                return accu + item
            }, 0)
        }
    }

    let p = new Person()
    console.log(p.name);
    console.log(2222, p.sum(['1', '2', '3']));
}

/** ========== 函数参数装饰器 参数装饰器  ===========*/
namespace E {
    /**  函数参数的装饰器, 其本质也是一个函数,接收三个参数
     * @param target 装饰静态成员, target就是构造函数,  装饰非静态成员, target就是构造函数原型.  ,
     * @param methodName 方法的名称
     * @param paramIndex 参数的索引
     */
    function addAge(target: any, methodName, paramIndex: number) {
        console.log(target, methodName, paramIndex, 999);
        target.age = 10
    }

    class Person {
        age: number

        login(username: string, @addAge password: string) {
            console.log(username, password);
        }
    }

    const p = new Person()
    console.log(p.age);
}

/** ========== =================== 装饰器的执行顺序 ======================*/
/**
 * 当有多个装饰器的时候
 *  类装饰器是最后执行的. 而且后写类装饰器,先执行
 *  方法和参数中的装饰器, 是先执行参数装饰器.
 *  方法和属性装饰器, 是谁在前面,先执行谁.
 *
 *  一般来说, 从外往内写,  从内往外执行
 */
namespace f {
    function ClassDecorator1() {
        return function (target) {
            console.log("类装饰器 一 1111");
        }
    }

    function ClassDecorator2() {
        return function (target) {
            console.log("类装饰器 二 2222");
        }
    }

    function PropertyDecorator(name: string) {
        return function (target, propertyName) {
            console.log("属性装饰器 PropertyDecorator", propertyName, name);
        }
    }

    function MethodDecorator() {
        return function (target, propertyName) {
            console.log("方法 MethodDecorator", propertyName);
        }
    }

    function ParameterDecorator() {
        return function (target, methodName, index) {
            console.log("参数装饰器 ParameterDecorator", methodName, index);
        }
    }

    @ClassDecorator1()
    @ClassDecorator2()
    class Person {
        @PropertyDecorator("name")
        name: string = ''
        @PropertyDecorator("age")
        age: number = 10

        @MethodDecorator()
        hello(@ParameterDecorator() p1: string, @ParameterDecorator() p2: string) {

        }
    }
}
export {}